""" Contains a set of useful OcempGUI-compatible widgets.

	This module will only work if OcempGUI is available from PYTHONPATH.

	To check whether or not it is available, check the truth status of the
    `available` module attribute.
"""

try:
    import os.path
    from ocempgui.widgets import *
    from ocempgui.widgets.Constants import *
    from ocempgui.widgets.components import *
    available = True
    
    __all__ = (
           'available',
           'KeyValue',
           'KeyValueTable',
           'FileDialogEx'
   )          
    
    class KeyValue(HFrame):
        """ Allows one to input a key=value pair.
            
            Designed to work in conjunction with KeyValueTable objects, which is
            why there is a "Del" button on the right.
            
            Text for the key and value can be accessed as the `key` and `value`
            properties, respectively.
            
            The Entry widgets can be accessed directly as the `key_entry` and
            `value_entry` attributes.
            
            Changing the minsize or maxsize of the KeyValue object changes the size
            of the two children to match.
        """
        def __init__(self,key='',value=''):
            HFrame.__init__(self)
            self.border = 0
            self.key_entry = Entry(key)
            self.key_entry.border = 1
            self.value_entry = Entry(value)
            self.value_entry.border = 1
            self.del_button = Button('Del')
            self.add_child(self.key_entry,self.value_entry,self.del_button)
            
        key = property(lambda self:       self.key_entry.text,
                       lambda self, text: self.value_entry.set_text(text))
        value = property(lambda self:       self.value_entry.text,
                         lambda self, text: self.value_entry.set_text(text))
        
        def set_minimum_size(self,w,h):
            HFrame.set_minimum_size(self,w,h)
            self.key_entry.set_minimum_size(int(w/2.3),h)
            self.value_entry.set_minimum_size(int(w/2.3),h)
        minsize = property(lambda self: (self._minwidth,self._minheight),
                           lambda self,size: self.set_minimum_size(*size))
        
        def set_maximum_size(self,w,h):
            HFrame.set_maximum_size(self,w,h)
            self.key_entry.set_maximum_size(int(w/2.3),h)
            self.value_entry.set_maximum_size(int(w/2.3),h)
            self.del_button.set_maximum_size(int(w*0.6),h)
        maxsize = property(lambda self: (self._maxwidth,self._maxheight),
                           lambda self,size: self.set_maximum_size(*size))
    
    class KeyValueTable(ScrolledWindow):
        """ Allows one to input any number number of 'key'='value' pairs.
            
            Appears as a box containing a list of editable key-value pair boxes,
            each with a delete button on the right. Pressing this will remove the
            row. There is also an add button at the bottom of the list to add a new,
            blank row (which corresponds to a key-value pair ''->'').
            
            A KeyValueTable object supports most of the methods of a dictionary.
            It supports mapping, adding of new values (each new entry will appear
            as a new row added to the bottom of the list,) changing of existing
            values (and changing of keys through the GUI) and deletion of existing
            values. It also supports iteration, the iteritems method, and the keys
            and values methods.
            
            Keys and values are taken directly from/set directly to their
            corresponding KeyValue widgets.
        """
        def __init__(self,w,h,rowH,defaults=None):
            # Initialize defaults
            ScrolledWindow.__init__(self,w,h)
            self._rowH = rowH
            # Create the inner frame
            self.frame = VFrame()
            self.frame.border = 0
            # Create the add button
            self.add_button = Button('Add')
            self.add_button.connect_signal(SIG_CLICKED,
                                           lambda me=self: me.__setitem__('',''))
            # Create the outer frame
            frame_outer = VFrame()
            frame_outer.border = 0
            frame_outer.align = ALIGN_LEFT
            frame_outer.add_child(self.frame, self.add_button)
            # Put it all together
            self.set_child(frame_outer)
            # Add the given starting rows
            if defaults is not None:
                for k,v in defaults.iteritems():
                    self[k] = v
        
        def notify(self,event):
            ScrolledWindow.notify(self,event)
            if event.signal == SIG_MOUSEDOWN:
                event.handled = False
                self.frame.notify(event)
        
        def __getitem__(self,key):
            for obj in self.frame.children:
                if obj.key == key:
                    return obj.value
            raise KeyError('No entry with key "%s"'%key)
        
        def __setitem__(self,key,val):
            for obj in self.frame.children:
                if obj.key == key:
                    obj.value = val
                    return
            else:
                kv = KeyValue(key,val)
                kv.minsize = kv.maxsize = self.w-32, self._rowH
                # Connect button to method that will delete this entry 
                kv.del_button.connect_signal(SIG_CLICKED,
                                             lambda me=self,o=kv: me.__delitem__(o.key))
                self.frame.add_child(kv)
        
        def __delitem__(self,key):
            for obj in self.frame.children:
                if obj.key == key:
                    self.frame.remove_child(obj)
                    return
            raise KeyError('No entry with key "%s"'%key)
        
        def __contains__(self,key):
            for obj in self.frame.children:
                if obj.key == key:
                    return True
            return False
        
        def __iter__(self):
            return iter(dict([(obj.key,obj.value) for obj in self.frame.children]))
        
        def keys(self):
            return [obj.key for obj in self.frame.children]
        
        def values(self):
            return [obj.value for obj in self.frame.children]
        
        def iteritems(self):
            return dict([(obj.key,obj.value) for obj in self.frame.children]).iteritems()
        
        def empty(self):
            """ Removes all items in the table. """
            for k in self.keys():
                del self[k]

    class FileDialogEx(FileDialog):
        """ An enhanced version of ocempgui.widgets.FileDialog.
            
            This version has a textbox at the bottom which can be used to type
            in filenames directly. Selecting an entry in the list will replace
            the text in the box. Multiple selections will create a list of
            semicolon-separated filenames in the GUI. However, the
            get_filenames() method will still behave as expected, and now has
            the capability to return names typed into the box at the bottom.
            
            Also, the constructor has been modified to accept a size for the
            dialog which will be used to determine the minimum size of all
            children.
            
            NOTES:
            * The size is a *seuqnce*, not a pair of arguments, as in some
              other widget constructors.)
            
            FIXME: The size does not come out to be the exact size of the dialog.
        """
        def __init__(self,size,title,buttons,responses,defaultDir=None):
            FileDialog.__init__(self,title,buttons,responses,defaultDir)
            # Create some shortcuts
            path_label = self.path_entry = self.content.children[0].children[0]
            path_entry = self.path_entry = self.content.children[0].children[1]
            file_list = self.file_list = self.content.children[1]
            # Add the new entry
            file_entry = self.file_entry = Entry()
            self.content.insert_child(2,file_entry)
            file_list.connect_signal(SIG_SELECTCHANGED,self._update_text)
            # Size everything
            textH = path_entry.h
            buttonH = self.content.children[2].h
            path_entry.minsize = size[0]-path_label.w, textH
            file_entry.minsize = size[0], textH
            file_list.minsize = size[0], size[1]-(textH*2)-buttonH-24
    
        def _update_text(self):
            """ Internal function.
                Updates the text of the file name entry to match the files
                selected.
            """
            filenames = [os.path.basename(n) for n in
                          FileDialog.get_filenames(self)]
            text = ';'.join(filenames)
            self.file_entry.text = text
        
        def get_filenames(self):
            """ Returns the list of selected filenames.
                This is the same as FileDiaolg.get_filenames, but it actually
                depends on the text in the Entry at the bottom of the dialog
                rather than the selections in the list. This means that you
                can expect to get filenames that do not actually exist (i.e.
                if the user typed a name that didn't exist).
            """
            names = [os.path.join(self.path_entry.text, n) for n in
                      self.file_entry.text.split(';')]
            if len(names) == 1 and names[0] == '':
                return []
            return names
    
#    class PopupMenu

except ImportError:
    available = False
